---
title: Custom scalar types in Apollo Kotlin
---

In addition to its [built-in scalar types](https://graphql.org/learn/schema/#scalar-types) (`Int`, `String`, etc.), GraphQL supports defining [custom scalars](/apollo-server/schema/custom-scalars/). For example, your schema might define a custom scalar for `Long`, `Date`, `BigDecimal`, or `GeoPoint`.

## Define class mapping

To interact with custom scalars in your Apollo Kotlin app, you need to define a mapping in your `build.gradle[.kts]` file. This tells Apollo Kotlin which class to use to represent each custom scalar from your schema.

```kotlin title="build.gradle[.kts]"
apollo {
  service("service") {
    mapScalar("GeoPoint", "com.example.GeoPoint")

    // Shortcuts exist for standard types - equivalent to mapScalar("Long", "kotlin.Long")
    mapScalarToKotlinLong("Long")
  }
}
```

> If needed, you can also do this with a built-in scalar (such as `ID`) to override its default type.

## Define class adapters

Each class you use to represent a custom scalar also requires an **adapter** to convert it to and from the JSON format that's sent over the network.

Each adapter requires a `fromJson` function. A `toJson` function is also required if your app ever passes the custom scalar as a GraphQL argument.

Here's an adapter for a `GeoPoint` custom scalar:

```kotlin
class GeoPoint(val latitude: Double, val longitude: Double)

val geoPointAdapter = object : Adapter<GeoPoint> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): GeoPoint {
    var latitude: Double? = null
    var longitude: Double? = null
    reader.beginObject()
    while(reader.hasNext()) {
      when (reader.nextName()) {
        "latitude" -> latitude = reader.nextDouble()
        "longitude" -> longitude = reader.nextDouble()
      }
    }
    reader.endObject()

    // fromJson can throw on unexpected data and the exception will be wrapped in a
    // ApolloParseException
    return GeoPoint(latitude!!, longitude!!)
  }

  // If you do not expect your scalar to be used as input, you can leave this method as TODO()
  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: GeoPoint) {
    writer.beginObject()
    writer.name("latitude").value(value.latitude)
    writer.name("longitude").value(value.longitude)
    writer.endObject()
  }
}
```

If you prefer working with `Map`s, Apollo Kotlin comes with `AnyAdapter`, which supports adapting `String`, `Int`, `Double`, `Boolean`, `List`, and `Map`. You can use it in an intermediate step:

```kotlin
val geoPointAdapter = object : Adapter<GeoPoint> {
  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): GeoPoint {
    val map = AnyAdapter.fromJson(reader) as Map<String, Double>
    return GeoPoint(map["latitude"] as Double, map["longitude"] as Double)
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: GeoPoint) {
    val map = mapOf(
        "latitude" to value.latitude,
        "longitude" to value.longitude
    )
    AnyAdapter.toJson(writer, map)
  }
}
```

This solution is more concise but slightly less performant.

## Register adapters

After you define your adapters, you need to register them. This can be done either in the `build.gradle[.kts]` file, or at runtime.

### In `build.gradle[.kts]`

A third argument can be passed to `mapScalar` to specify the adapter to use:

```kotlin title="build.gradle[.kts]"
apollo {
  service("service") {
    mapScalar("GeoPoint", "com.example.GeoPoint", "com.example.Adapters.geoPointAdapter")
  }
}
```

The given expression is copied as-is in the generated code. Therefore, it's possible to pass any of the following:

* An instantiation expression, like `"com.example.GeoPointAdapter()"`
* A singleton reference, like `"com.example.GeoPointAdapter"`
* A function call, like `"com.example.Adapters.getGeoPointAdapter()"`

Make sure you pass the full class name including the package, because imports aren't automatically generated.

### At runtime

You can also register adapters on your `ApolloClient` instance by calling `ApolloClient.Builder.addCustomScalarAdapter` once for each adapter:

```kotlin
val apolloClient = ApolloClient.Builder().serverUrl("https://example.com/graphql")
    .addCustomScalarAdapter(GeoPoint.type, geoPointAdapter)
    .build()
```

This method takes a type-safe generated class from `Types`, along with its corresponding adapter.

> If you can't find `Types`, build your project to trigger codegen.

## Built-in adapters

`apollo-api` provides the following built-in adapters:

| Adapter                                       | Description                                           |
|-----------------------------------------------|-------------------------------------------------------|
| `com.apollographql.apollo.api.StringAdapter`  | Converts from/to `kotlin.String`/`java.lang.String`   |
| `com.apollographql.apollo.api.IntAdapter`     | Converts from/to `kotlin.Int`/`java.lang.Int`         |
| `com.apollographql.apollo.api.BooleanAdapter` | Converts from/to `kotlin.Boolean`/`java.lang.Boolean` |
| `com.apollographql.apollo.api.DoubleAdapter`  | Converts from/to `kotlin.Double`/`java.lang.Double`   |
| `com.apollographql.apollo.api.FloatAdapter`   | Converts from/to `kotlin.Float`/`java.lang.Float`     |
| `com.apollographql.apollo.api.LongAdapter`    | Converts from/to `kotlin.Long`/`java.lang.Long`       |
| `com.apollographql.apollo.api.AnyAdapter`     | Converts from/to `kotlin.Any`/`java.lang.Object`      |

## Extra adapters

Extra adapters for dates and big decimals are available at https://github.com/apollographql/apollo-kotlin-adapters
